package jcolibri.extensions.maintenance_evaluation;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import jcolibri.cbrcore.CBRQuery;
import jcolibri.evaluation.EvaluationReport;
import jcolibri.method.maintenance.QueryResult;

import org.apache.commons.logging.LogFactory;

/**
 * This class stores the result of each query in an evaluation. 
 * It is configured and filled by an Evaluator.
 * 
 * @author Lisa Cummins.
 */
public class DetailedEvaluationReport extends EvaluationReport 
{
    /** Stores the query series info */
    protected HashMap queryData;

    /**
     * Creates a new report.
     */
    public DetailedEvaluationReport()
    {	super();
    	queryData = new HashMap();
    }
	
    /**
     * Returns the evaluation info identified by the given label.
     * @param label identifies the evaluation series.
     * @return the evaluation info identified by the given label.
     */
    public List getQuerySeries(String label)
    {   return (List)queryData.get(label);
    }
    
    /**
     * Sets the given evaluation series to be the series identified by the 
     * given label.
     * @param label identifier of the evaluation series.
     * @param queryEvaluations the evaluation series. 
     */
    public void setSeries(String label, List queryEvaluations)
    {	queryData.put(label, queryEvaluations);
    }
    
    /**
     * Adds the given query and value to the series labelled by the given label.
     * @param label the label whose series the query and value are being added to.
     * @param query the query.
     * @param value the query's value.
     */
    public void addDataToSeries(String label, CBRQuery query, Double value)
    {	List queries = (List)queryData.get(label);
    	if(queries == null)
    	{	queries = new LinkedList();
    	}
    	queries.add(new QueryResult(query, value));
    	queryData.put(label, queries);
    }
    
    /** 
     * Returns the names of the contained evaluation series.
     * @return the names of the contained evaluation series.
     */
    public String[] getQuerySeriesLabels()
    {   Set set = queryData.keySet();
        String[] res = new String[set.size()];
        int i=0;
        for(Object o : set){
        	String e = (String)o;
        	res[i++] = e;
        }
            
        return res;
    }

    /**
     * Prints the report to the given file.
     * @param filename the file to print the report to.
     */
    public void printDetailedEvaluationReport(String filename)
    {	PrintWriter pw = null;
		try
		{	pw = new PrintWriter(new BufferedWriter(new FileWriter(filename, true)));
	    	
			pw.println("Results:");
			String[] seriesLabels = getSeriesLabels();
			String[] querySeriesLabels = getQuerySeriesLabels();
			String[] otherLabels = getOtherLabels();
		    	
			for(int i=0; i<seriesLabels.length; i++)
		    	{	pw.println(seriesLabels[i]+ ":");
	    			Vector results = getSeries(seriesLabels[i]);
	    			String series = "";
	    			for(Object o: results){
	    				Double result=(Double)o;
	    				series += result + ",";
	    			}
	    			     
	    			pw.println(series.substring(0, series.length()-2));
	    			pw.println("Average: " + getAverageOfDataSeries(seriesLabels[i]));
	    			pw.println();
		    	}
			pw.println();
	
			for(int i=0; i<querySeriesLabels.length; i++)
		    	{	pw.println(querySeriesLabels[i]+ ":");
	    			List results = getQuerySeries(querySeriesLabels[i]);
	    			for(Object o: results){
	    				QueryResult qResult = (QueryResult)o;
	    				pw.println(qResult.getCase().getID() + ": " + qResult.getResult());
	    			}
	    			     
	    			pw.println("Average: " + getAverageOfQueryDataSeries(querySeriesLabels[i]));
	    			pw.println();
		    	}
			pw.println();
			
			for(int i=0; i<otherLabels.length; i++)
	    	{	pw.println(otherLabels[i]+ ": " + getOtherData(otherLabels[i]));
    			pw.println();
	    	}
	    } catch(IOException ioe) {
    	}
    	finally 
    	{	if(pw != null)
    			pw.close();
    	}
    }
	
    /**
     * Removes a data serie.
     * @param label the label of the data series to remove.
     */
    public void removeDataSeries(String label)
    {	data.remove(label);
    }
	
    /**
     * Removes some data.
     * @param label the label of the data to remove.
     */
    public void removeOtherData(String label)
    {	other.remove(label);
    }

    /**
     * Returns the average of the data series with the given label. If
     * the label given is not the label of any data series, null will
     * be returned and an error message will be printed.
     * @param label the label of the data series.
     * @return the average of the given data series.
     */
    public Double getAverageOfDataSeries(String label)
    {	Vector v = (Vector)data.get(label);
    	if(v == null)
    	{	LogFactory.getLog(this.getClass()).error("Data series by this label does not exist");
    		return null;
    	}
    	if(v.size() == 0)
    	{	return 0.0;
    	}
    	double total = 0.0;
    	for(Object o: v){
    		Double value = (Double)o;
    		total += value;
    	}
	return total / v.size();
    }
	
    /**
     * Returns the average of the query data series with the given label. If
     * the label given is not the label of any data series, null will
     * be returned and an error message will be printed.
     * @param label the label of the query data series.
     * @return the average of the given query data series.
     */
    public Double getAverageOfQueryDataSeries(String label)
    {	List results = (List)queryData.get(label);
    	if(results == null)
    	{	LogFactory.getLog(this.getClass()).error("Data series by this label does not exist");
    		return null;
    	}
    	if(results.size() == 0)
    	{	return 0.0;
    	}
    	double total = 0.0;
    	for(Object o: results){
    		QueryResult result = (QueryResult)o;
    		total += result.getResult();    	
    	}
	return total / results.size();
    }
	
    /**
     * Returns the String representation of this report.
     * @return the String representation of this report. 
     */
    public String toString()
    {
    	StringBuffer s = new StringBuffer();
    	s.append("Series:\n");
    	String[] series = this.getSeriesLabels();
    	for(int i=0; i<series.length; i++)
    	{	s.append("  "+series[i]+": \n    ");
    		Vector v = this.getSeries(series[i]);
    		for(Object o: v){
    			Double d = (Double)o;
    			s.append(d+",");
    		}
    			
    		s.append("\n");
    		s.append("  Average: " + getAverageOfDataSeries(series[i]) + "\n\n");
    	}
    	
    	s.append("\nOther data:\n");
    	String[] other = this.getOtherLabels();
    	for(int i=0; i<other.length; i++)
    	{
    		s.append("  "+other[i]+": "+ this.getOtherData(other[i])+"\n");
    	}
    	
    	s.append("\nNumber of Cycles: "+ this.getNumberOfCycles());
    	s.append("\nTime per Cycle:   "+ this.getTimePerCycle()+" ms");
    	s.append("\nTotal time:       "+ this.getTotalTime()+" ms");
    	
    	return s.toString();
    }
}